package com.general.client.widgets.textbox;

import java.util.ArrayList;

import com.general.client.basewidget.BaseWidget;
import com.general.client.bundle.CommonBundle;
import com.general.client.enableable.Enableable;
import com.general.client.hasinterfaces.HasHint;
import com.general.client.modes.HasModes;
import com.general.client.modes.WidgetMode;
import com.general.client.position.Position;
import com.general.client.validator.HasValidators;
import com.general.client.validator.NonEmptyStringValidator;
import com.general.client.validator.Validator;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HasValue;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Widget;

public class TextBox extends BaseWidget implements HasValue<String>, HasHint,
		HasValidators, HasModes, Enableable {

	private static TextBoxUiBinder uiBinder = GWT.create(TextBoxUiBinder.class);

	interface TextBoxUiBinder extends UiBinder<Widget, TextBox> {
	}

	private String hintString = "";
	private boolean showHint = true;

	private boolean showLabel = true;
	private Position labelPosition = Position.LEFT;

	private boolean error = false;

	private boolean isMandatory = false;

	NonEmptyStringValidator mandatoryValidator = null;

	@UiField
	Label textBoxView;

	@UiField
	com.google.gwt.user.client.ui.TextBox textBox;

	@UiField
	Label astrix;

	@UiField
	Label label;

	@UiField
	FlowPanel labelContainer;

	@UiField
	CommonBundle bundle;

	@UiField
	FlowPanel topContainer;

	private final ArrayList<Validator> validators = new ArrayList<Validator>();
	private HandlerRegistration addValueChangeHandlerRegistration = null;
	private WidgetMode mode = WidgetMode.EDIT;
	private HandlerRegistration onBlurSwitchToViewRegistration;
	private HandlerRegistration onClickSwitchToEditRegistration;

	public TextBox() {
		initWidget(uiBinder.createAndBindUi(this));

		// label defaults to show, left
		setLabelPosition(Position.LEFT);
		setShowLabel(true);

		// non mandatory by default
		setMandatory(false);

		setStyleHint();

		setMode(mode);
		textBox.setText(hintString);
		// focus
		textBox.addFocusHandler(new FocusHandler() {

			@Override
			public void onFocus(FocusEvent event) {
				setInFocus();
			}
		});
		// click
		textBox.addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				setInFocus();
			}
		});
		// blur
		textBox.addBlurHandler(new BlurHandler() {

			@Override
			public void onBlur(BlurEvent event) {
				if (textBox.getText().isEmpty()) {
					setStyleHint();
					textBox.setText(hintString);
				} else {
					setStyleNormal();
				}
			}
		});
	}

	private void setStyleHint() {
		textBox.addStyleName(bundle.text().hintText());
		textBox.removeStyleName(bundle.text().normalText());

		textBoxView.addStyleName(bundle.text().hintText());
		textBoxView.removeStyleName(bundle.text().normalText());
	}

	private void setStyleNormal() {
		textBox.addStyleName(bundle.text().normalText());
		textBox.removeStyleName(bundle.text().hintText());

		textBoxView.addStyleName(bundle.text().normalText());
		textBoxView.removeStyleName(bundle.text().hintText());
	}

	private void setInFocus() {
		if (textBox.getText().equals(hintString)) {
			setStyleNormal();
			textBox.setText("");
		}
	}

	public void setFocus(boolean focus) {
		if (focus && isEnabled()) {
			textBox.setFocus(focus);
		}
	}

	/**
	 * HasValue<String> methods
	 */
	@Override
	public HandlerRegistration addValueChangeHandler(
			final ValueChangeHandler<String> handler) {
		return textBox.addValueChangeHandler(new ValueChangeHandler<String>() {

			@Override
			public void onValueChange(ValueChangeEvent<String> event) {
				// call the change handler only if the new value is valid
				if (isValid()) {
					handler.onValueChange(event);
				}

			}
		});
	}

	@Override
	public String getValue() {
		if (textBox.getText().equals(hintString)) {
			return "";
		}
		return textBox.getText();
	}

	@Override
	public void setValue(String value) {
		setValue(value, false);
	}

	@Override
	public void setValue(String value, boolean fireEvents) {
		if (value == null || value.isEmpty()) {
			setStyleHint();
		} else {
			setStyleNormal();
		}
		textBox.setValue(value, fireEvents);
		textBoxView.setText(value);
	}

	/**
	 * HasHint methods
	 */
	@Override
	public void setHint(String hint) {
		boolean refreshHint = false;
		if (textBox.getValue().equals(hintString))
			refreshHint = true;
		hintString = hint;

		if (refreshHint)
			textBox.setValue(hint);
	}

	@Override
	public boolean isShowHint() {
		return showHint;
	}

	@Override
	public void setShowHint(boolean showHint) {
		this.showHint = showHint;
	}

	/**
	 * Label methods
	 */
	public String getLabel() {
		return label.getText();
	}

	public void setLabel(String labelString) {
		label.setText(labelString);
	}

	public boolean isShowLabel() {
		return showLabel;
	}

	public void setShowLabel(boolean showLabel) {
		this.showLabel = showLabel;
		labelContainer.setVisible(showLabel);
	}

	/**
	 * label position methods
	 */
	public Position getLabelPosition() {
		return labelPosition;
	}

	public void setLabelPosition(Position labelPosition) {
		this.labelPosition = labelPosition;
		if (labelPosition.equals(Position.ABOVE)) {
			textBox.addStyleName(bundle.style().positionAbove());
			textBoxView.addStyleName(bundle.style().positionAbove());
		} else if (labelPosition.equals(Position.LEFT)) {
			textBox.addStyleName(bundle.style().positionLeft());
			textBoxView.addStyleName(bundle.style().positionLeft());
		}
	}

	/**
	 * error mode methods
	 */
	public boolean isError() {
		return error;
	}

	public void setError(boolean error) {
		this.error = error;
		if (error) {
			if (mode.equals(WidgetMode.EDIT)) {
				textBox.setFocus(true);
			}
			textBox.addStyleName(bundle.style().error());
			textBoxView.addStyleName(bundle.style().error());
		} else {
			textBox.removeStyleName(bundle.style().error());
			textBoxView.removeStyleName(bundle.style().error());
		}
	}

	/**
	 * HasValidators method
	 */
	@Override
	public void addValidator(Validator validator) {
		this.validators.add(validator);
	}

	@Override
	public boolean isValid() {
		if (validators == null) {
			setError(false);
			return true;
		}
		for (Validator validator : validators) {
			if (!validator.isValid(getValue())) {
				setError(true);
				return false;
			}
		}
		setError(false);
		return true;
	}

	@Override
	public void setValidateOnValueChange(boolean validateOnValueChange) {
		if (validateOnValueChange && addValueChangeHandlerRegistration == null) {
			addValueChangeHandlerRegistration = addValueChangeHandler(new ValueChangeHandler<String>() {

				@Override
				public void onValueChange(ValueChangeEvent<String> event) {
					isValid();
				}
			});
		} else if (addValueChangeHandlerRegistration != null) {
			addValueChangeHandlerRegistration.removeHandler();
			addValueChangeHandlerRegistration = null;
		}
	}

	/**
	 * overrides
	 */
	@Override
	public boolean isVisible() {
		return topContainer.isVisible();
	}

	@Override
	public void setVisible(boolean visible) {
		topContainer.setVisible(visible);
		super.setVisible(visible);
	}

	/**
	 * delegators
	 */
	@Override
	public boolean isEnabled() {
		return textBox.isEnabled();
	}

	@Override
	public void setEnabled(boolean enabled) {
		textBox.setEnabled(enabled);
		if (enabled) {
			textBox.removeStyleName(bundle.style().disabled());
			textBoxView.removeStyleName(bundle.style().disabled());
		} else {
			textBox.addStyleName(bundle.style().disabled());
			textBoxView.addStyleName(bundle.style().disabled());
		}
	}

	/**
	 * various
	 */
	public boolean isMandatory() {
		return isMandatory;
	}

	public void setMandatory(boolean isMandatory) {
		this.isMandatory = isMandatory;
		astrix.setVisible(isMandatory);
		if (isMandatory) {
			mandatoryValidator = new NonEmptyStringValidator();
			validators.add(mandatoryValidator);
		} else {
			validators.remove(mandatoryValidator);
			mandatoryValidator = null;
		}
	}

	@Override
	public WidgetMode getMode() {
		return this.mode;
	}

	@Override
	public void setMode(WidgetMode mode) {
		this.mode = mode;
		if (mode.equals(WidgetMode.EDIT)) {
			setValue(textBoxView.getText());
			textBoxView.setVisible(false);
			textBox.setVisible(true);
			textBox.setFocus(true);
		} else {
			textBoxView.setText(textBox.getValue());
			textBoxView.setVisible(true);
			textBox.setVisible(false);
		}
	}

	@Override
	public void setOnClickSwitchToEdit(boolean onFocusSwitchToEdit) {
		if (onFocusSwitchToEdit && onClickSwitchToEditRegistration == null) {
			onClickSwitchToEditRegistration = textBoxView
					.addClickHandler(new ClickHandler() {

						@Override
						public void onClick(ClickEvent event) {
							if (isEnabled()) {
								setMode(WidgetMode.EDIT);
							}
						}
					});
		} else if (onClickSwitchToEditRegistration != null) {
			onClickSwitchToEditRegistration.removeHandler();
		}
	}

	@Override
	public void setOnBlurSwitchToView(boolean onBlurSwitchToView) {
		if (onBlurSwitchToView && onBlurSwitchToViewRegistration == null) {
			onBlurSwitchToViewRegistration = textBox
					.addBlurHandler(new BlurHandler() {

						@Override
						public void onBlur(BlurEvent event) {
							setMode(WidgetMode.VIEW);
						}
					});
		} else if (onBlurSwitchToViewRegistration != null) {
			onBlurSwitchToViewRegistration.removeHandler();
		}
	}
}
